/*
 * sr_get.c - SR get related stuff
 * Clunix, cezanne@clunix.com, 2008.3
 *
 * Copyright (c) 2001-2008 by Clunix Inc. All Rights Reserved.
 */

#include "config.h"

#include "sr.h"
#include "sr_p.h"

static sr_t * lookup(sr_t *sr, LNPath_t *LNPath, int idx, ECMBOOL single);

static sr_t *
lookup_all(sr_t *sr, LNPath_t *LNPath, int idx, ECMBOOL single)
{
	sr_t	*sr_child, *sr_child_res;
	sr_t	*sr_res = NULL;

	if (!sr_is_HMSgroup(sr))
		return NULL;

	SR_FOREACH_CHILD(sr, NULL, sr_child) {
		sr_child_res = lookup(sr_child, LNPath, idx + 1, single);
		if (sr_child_res == NULL)
			continue;
		if (single)
			return sr_child_res;
		if (sr_is_vHMS(sr_child_res)) {
			if (sr_res == NULL)
				sr_res = sr_child_res;
			else {
				sr_merge_unique(sr_res, sr_child_res);
				sr_free(sr_child_res);
			}
		}
		else {
			if (sr_res == NULL)
				sr_res = sr_create_vHMS(sr_get_type(sr_child_res));
			if (sr_is_virtual(sr_child_res))
				SR_APPEND_ASSERT(sr_res, sr_dup(sr_child_res));
			else
				SR_APPEND_ASSERT(sr_res, sr_build_virtual(sr_child_res));
		}
	}
	return sr_res;
}

static sr_t *
lookup_any(sr_t *sr)
{
	if (!sr_is_HMSgroup(sr))
		return NULL;
	return sr_first(sr);
}

static sr_t *
lookup_parent(sr_t *sr)
{
	sr_t	*sr_parent;

	sr_parent = sr_get_parent(sr);
	if (sr_parent)
		return sr_parent;
	return sr;
}

static sr_t *
lookup_child(sr_t *sr, const char *name)
{
	return sr_get_child(sr, name);
}

static sr_t *
lookup(sr_t *sr, LNPath_t *LNPath, int idx, ECMBOOL single)
{
	LNI_t	*LNI;
	int	lastidx = LNPath->n_LNI - 1;

	while (idx <= lastidx) {
		LNI = LNPath->LNIs + idx;

		if (sr_is_ref(sr))
			sr = sr_get_deref(sr);

		switch (LNI->type) {
		case LNI_TYPE_ALL:
			return lookup_all(sr, LNPath, idx + 1, single);
		case LNI_TYPE_ANY:
			sr = lookup_any(sr);
			break;
		case LNI_TYPE_DOTDOT:
			sr = lookup_parent(sr);
			break;
		case LNI_TYPE_NAME:
			sr = lookup_child(sr, LNI->name);
			break;
		default:
			return NULL;
		}
		if (sr == NULL)
			return NULL;
		idx++;
	}

	if (sr_is_ref(sr) && !LNPath->is_ref)
		sr = sr_get_deref(sr);

	return sr;
}

static sr_t *
lookup_with_LNPath(sr_t *sr_anchor, LNPath_t *LNPath, ECMBOOL single)
{
	sr_t	*sr_base = NULL;
	sr_t	*sr_res;

	if (LNPath->type == LNP_TYPE_ABS)
		sr_base = sr_get_root(sr_anchor);
	else if (sr_is_group(sr_anchor))
		sr_base = (sr_t *)sr_anchor;
	else
		sr_base = sr_get_parent(sr_anchor);

	if (sr_base == NULL) {
		free_LNP(LNPath);
		return NULL;
	}
	sr_res = lookup(sr_anchor, LNPath, 0, single);
	free_LNP(LNPath);
	return sr_res;
}

static sr_t *
do_getn(const sr_t *sr_anchor, const char *LN, ECMBOOL single)
{
	LNPath_t	*LNPath;

	if (sr_anchor == NULL || LN == NULL)
		return NULL;

	LNPath = parse_LNP(LN);
	if (LNPath == NULL)
		return NULL;

	return lookup_with_LNPath((sr_t *)sr_anchor, LNPath, single);
}

static sr_t *
do_get(const sr_t *sr_anchor, const sr_t *sr_LNP, ECMBOOL single)
{
	char	*LNP;
	sr_t	*sr_res;

	if (sr_anchor == NULL || sr_LNP == NULL)
		return NULL;

	LNP = string_LNP(sr_LNP);
	if (LNP == NULL)
		return NULL;

	sr_res = do_getn(sr_anchor, LNP, single);
	ecm_free(LNP);
	return sr_res;
}

sr_t *
sr_get(const sr_t *sr_anchor, const sr_t *sr_LNP)
{
	if (is_ABS_sr_LNP(sr_LNP))
		return do_get(sr_get_root(sr_anchor), sr_LNP, ECM_FALSE);
	return do_get(sr_anchor, sr_LNP, ECM_FALSE);
}

sr_t *
sr_get1(const sr_t *sr_anchor, const sr_t *sr_LNP)
{
	return do_get(sr_anchor, sr_LNP, ECM_TRUE);
}

sr_t *
sr_getn(const sr_t *sr_anchor, const char *LN)
{
	return do_getn(sr_anchor, LN, ECM_FALSE);
}

sr_t *
sr_getn1(const sr_t *sr_anchor, const char *LN)
{
	return do_getn(sr_anchor, LN, ECM_TRUE);
}
